package com.iqi.onigao.netty;

import java.net.SocketAddress;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import com.iqi.onigao.App;
import com.iqi.onigao.constant.SessionConstant;
import com.iqi.onigao.module.SIOSimpleHandler;
import com.iqi.onigao.thread.ThreadExecutor;

import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import io.netty.channel.Channel;
import io.netty.util.AttributeKey;
import net.IClient;
import net.Package;
import si.ModuleId;
import si.pod.PlayerPOD;

public class NettySession implements IClient {
	
	
	static final Log log = LogFactory.get(NettySession.class);
	
	public static final AttributeKey<NettySession> sessions = AttributeKey.valueOf("session");
	
	private static AtomicLong idCreator = new AtomicLong(0);
	
	private final Channel channel;
	
	private final long id;
	
	private volatile boolean isClosing = false;
	
	private Map<Object, Object> attributes = new ConcurrentHashMap<>();

	public NettySession(Channel channel,String account) {
		this.channel = channel;
		this.id = idCreator.incrementAndGet();
		setAttr(SessionConstant.ACCOUNT, account);
	}

	public Map<Object, Object> getAttributes() {
		return attributes;
	}

	@Override
	public Object getAttribute(Object key) {
		if (key == null) {
			throw new IllegalArgumentException("key");
		}
		return attributes.get(key);
	}

	@Override
	public Object setAttribute(Object key, Object value) {
		if (key == null) {
			throw new IllegalArgumentException("key");
		}
		synchronized (attributes) {
			if (value == null) {
				return attributes.remove(key);
			}
			return attributes.put(key, value);
		}
	}

	@Override
	public Object getRealSession() {
		return channel;
	}

	@Override
	public Object setAttributeIfAbsent(Object key, Object value) {
		if (key == null) {
			throw new IllegalArgumentException("key");
		}

		if (value == null) {
			return null;
		}

		Object oldValue;
		synchronized (attributes) {
			oldValue = attributes.get(key);
			if (oldValue == null) {
				attributes.put(key, value);
			}
		}
		return oldValue;
	}

	@Override
	public Object removeAttribute(Object key) {
		if (key == null) {
			throw new IllegalArgumentException("key");
		}
		return attributes.remove(key);
	}

	@Override
	public Object setAttribute(Object key) {
		return setAttribute(key, Boolean.TRUE);
	}

	@Override
	public boolean isClosing() {
		return isClosing;
	}

	@Override
	public boolean isConnected() {
		return channel.isActive();
	}

	@Override
	public Long getId() {
		return id;
	}

	@Override
	public void close() {
		
		ThreadExecutor.execute(this, ()->{
			if (isConnected()) {
				channel.close();
			}else {
				try {
					// 关闭定时
					ScheduledFuture<?> future = getAttr(SessionConstant.FUTURE);
					if (future!=null) {
						future.cancel(true);						
					}
					isClosing = true;
					String account = getAttr(SessionConstant.ACCOUNT);
					log.info("玩家[{}]离开",account);
					
					// 10秒后再登录
					ThreadExecutor.scheduleAtFixedRate(()->{
						App.enterLoginServer(account);
					}, 5, TimeUnit.SECONDS);
				} catch (Exception e) {
					log.error(e);
				}
			}
		});
	}

	@Override
	public SocketAddress getRemoteAddress() {
		return channel.remoteAddress();
	}

	@Override
	public boolean send(Package p) {
		return send(p.getOutBytes());
	}

	@Override
	public boolean send(byte[] msg) {
		channel.writeAndFlush(msg);
		return true;
	}
	
	public void receive(byte[] msg) {
		Package pack = new Package();
		pack.init((byte[]) msg);
		ThreadExecutor.execute(this, ()->{
			PlayerPOD player = getAttr(SessionConstant.PLAYER);
			if (player == null) {
				if (pack.getHeader().getMessagid() != ModuleId.user_validateUUIDResult && 
						pack.getHeader().getMessagid() != ModuleId.user_createRoleResult && 
						pack.getHeader().getMessagid() != ModuleId.user_chooseRoleResult && 
						pack.getHeader().getMessagid() != ModuleId.player_loadPlayerResult) {
					return;
				}
			}
			SIOSimpleHandler.INS.process(this, pack);	
		});
	}

	@Override
	public void setMergeStart() {
	}

	@Override
	public void setMergeEnd() {
	}


	@Override
	public void setIgnoreMessage(boolean ignore) {
	}

	@Override
	public boolean isIgnoreMessage() {
		return false;
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T> T getAttr(String key) {
		return (T) attributes.get(key);
	}

	@Override
	public void setAttr(String key, Object value) {
		attributes.put(key, value);
	}

	@Override
	public void removeAttr(String key) {
		attributes.remove(key);
	}

	@Override
	public void clearAttr() {
		attributes.clear();
	}

}
